<?php
/**
 * Created by PhpStorm.
 * User: s.utkin
 * Date: 19.09.14
 * Time: 16:43
 */

namespace Resources\Workers;


//use NotificationDelivery\Notification\Workers\Base; // TODO: Вынести в ядро в очередь, но нужен рефакторинг, чтобы выделить и диспечера
use PublishedResources\PublishedPackagesConfig;
use Resources\Config\PackagesConfig;
use Resources\File;
use Resources\Group;
use Resources\Manager;
use Resources\Publisher;
use Resources\Queue;

class PublishPackageAction extends \Action
{
	protected $cachedElements = [];

	// TODO: Вынести в ядро в очередь, но нужен рефакторинг, чтобы выделить и диспечера
	protected $items;
	
	public function __construct(\Pinenut\Request $request){
		parent::__construct($request);
	
		$this->items = (array)$this->request->items;
	}
	

	/**
	 * Обрабатывает пачку уведомлений о необходимости опубликовать ресурс
	 *
	 * @return array
	 */
	
	public function run()
	{
		$this->response->noRender(true);
		$this->response->noCache(true);
		
		Publisher::$basePath = realpath(dirname($_SERVER['SCRIPT_FILENAME']) . '/../public/resources');
		if (!file_exists(Publisher::$basePath)) {
			mkdir(Publisher::$basePath, 0777, true);
		}

		Publisher::setRuntimeConfig([], [], true);

		$successList = [];
		# Перебираем массив получателей
		foreach ($this->items as $item) {
				# Пытаемся создать уведомления на отправку получателю
				//try {
					/*
					 * Если для очередного элемента очереди уведомления успешно созданы - добавляем
					 * id элемента в successList - список успешно обработанных элементов
					 */
					if ($this->process($item)) {
						$successList[] = (string)$item;
					}
//				} catch (\Exception $e) {
//				}
		}

		return $this->response->successList = $successList;
	}

	public function process(Queue $item)
	{
		$version = isset($item->data['version']) ? $item->data['version'] : 1;
		$packages = Publisher::getRuntimeConfig();
		$groups = Publisher::getRuntimeConfig(true);
		$package = isset($item->data['package']) ? $item->data['package'] : null;
		$group = isset($item->data['group']) ? $item->data['group'] : null;

		if ($package) {
			if (isset($packages[$package]) && ($packages[$package] >= $version)) {
				return true;
			}

			return $this->publishPackage($item->data['package'], $version);
		}

		if ($group) {
			if (isset($groups[$group]) && ($groups[$group] >= $version)) {
				return true;
			}

			return $this->publishGroup($item->data['group'], $version);
		}

		\Log::error('Package and group is not isset.');
		return true;
	}

	####################### GROUP #######################
	protected function publishGroup($groupName, $version)
	{
		$files = [
			'css' => [],
			'js' => [],
			'other' => [],
		];

		if (!isset(PackagesConfig::$groups[$groupName])) {
			\Log::error("Group '$groupName' not found.");
			return true;
		}

		# собираем относительные пути к файлам группы
		foreach (PackagesConfig::$groups[$groupName] as $package) {
			$packageConfigName = PackagesConfig::get($package);
			$packageConfigDirName = dirname(ltrim(str_replace('\\', '/', $packageConfigName), '/') . '.php');

			if (!method_exists($packageConfigName, 'get')) {
				echo "$package\n";
				echo "$packageConfigName\n\n";
				exit;
			}

			$jsFiles = $packageConfigName::get('js');
			$cssFiles = $packageConfigName::get('css');
			$otherFiles = $packageConfigName::get('files');

			foreach ((array)$jsFiles as $file) {
				if (isset($file['external']) && $file['external']) {
					continue;
				}
				$file['src'] = $packageConfigDirName . '/' . $file['src'];

				$files['js'][$file['position']][$file['src']] = $file['src'];
			}

			foreach ((array)$cssFiles as $file) {
				if (isset($file['external']) && $file['external']) {
					continue;
				}

				$file['src'] = $packageConfigDirName . '/' . $file['src'];

				$media = isset($file['media']) ? $file['media'] : '';
				$files['css'][$file['position']][$file['src']] = $media;
			}

			foreach ((array)$otherFiles as $file) {
				$filePath = $packageConfigDirName . '/' . $file;
				$files['other'][$packageConfigDirName][$file] = $filePath;
			}
		}

		$this->createGroupFiles($groupName, $files);

		return true;
	}

	protected function createGroupFiles($groupName, $files)
	{
		$groups = Publisher::getRuntimeConfig(true);

		if (!isset($groups[$groupName])) {
			$groups[$groupName] = 0;
		}

		$groupVersion = (int)$groups[$groupName] + 1;

		$groupFilesDir = realpath(PINENUT_APPLICATION . '/../public/resources') .
			'/' . $groupName . $groupVersion . '/';

		foreach (['js', 'css'] as $srcType) {
			if (!isset($files[$srcType])) {
				continue;
			}

			foreach ($files[$srcType] as $groupFilePosition => $items) {
				$groupFileName = realpath(
						PINENUT_APPLICATION . '/../public/resources'
					) . '/' . $groupName . $groupVersion . '/' . $srcType . '/' . $groupName . "_$groupFilePosition.$srcType";
				$groupDir = dirname($groupFileName);

				if (!file_exists($groupDir)) {
					if (!mkdir($groupDir, 0777, true)) {
						$msg = "Can't crate directory '$groupDir'";
						\Log::error(__CLASS__, $msg);
						echo $msg;
						ob_flush();
						continue;
					}
				}

				foreach ($items as $src => $data) {
					$realPath = Publisher::getRealFilePath($src);
					$filePtr = fopen($realPath, 'rb');
					while (!feof($filePtr)) {
						$data = fread($filePtr, 65536);
						file_put_contents($groupFileName, $data, FILE_APPEND);
					}
					file_put_contents($groupFileName, "\n\n", FILE_APPEND);
					fclose($filePtr);
				}
			}
		}

		foreach ((array)$files['other'] as $packageDir => $packageFiles) {
			foreach ($packageFiles as $mask) {
				$maskFiles = Manager::getFilesByRelativeMask($mask);

				foreach ((array)$maskFiles as $file) {
					$realSrcPath = Publisher::getRealFilePath($file);
					$tmp = explode($packageDir, $realSrcPath);
					$targetPath = $groupFilesDir . ltrim(isset($tmp[1]) ? $tmp[1] : '', '/');
					$targetPathDir = dirname($targetPath);

					# Создаем каталог, в который публикуется файл
					if (!file_exists($targetPathDir) && !mkdir($targetPathDir, 0777, true)) {
						throw new \Exception('Can`t create directory: ' . $targetPathDir, 500);
					}

					# Публикуем файл
						if (!copy($realSrcPath, $targetPath)) {
						throw new \Exception('Can`t copy file.', 500);
					}
				}
			}
		}

		$groups[$groupName] = $groupVersion;
		$packages = Publisher::getRuntimeConfig();
		Publisher::setRuntimeConfig($packages, $groups);
	}


	####################### PACKAGE #######################
	protected function publishPackage($packageName, $version)
	{
		# Получаем имя класса с конфигом пакета
		$packageClassName = PackagesConfig::get($packageName);
		$packageRelativeDirName = Publisher::getPackageRelativeDir($packageClassName);

		if (!class_exists($packageClassName) || !method_exists($packageClassName, 'get')) {
			throw new \Exception("Wrong package config: $packageClassName");
		}

		# Получаем конфиг пакета
		$packageConfig = $packageClassName::get();

		# Инкрементируем версию пакета
		$config = Publisher::getRuntimeConfig();
		$groups = Publisher::getRuntimeConfig(true);
		$packageVersion = 0;

		if (isset($config[$packageName])) {
			$packageVersion = (int)$config[$packageName];
		}

		$packageVersion++;
		$config[$packageName] = $packageVersion;
		Publisher::setRuntimeConfig($config, $groups);

		# Публикуем файлы
		$sourceTypeList = ['js', 'css', 'files'];

		foreach ($sourceTypeList as $sourceType) {
			if (isset($packageConfig[$sourceType])) {
				$this->publishSourceFiles($packageConfig[$sourceType], $packageName, $packageRelativeDirName);
			}
		}

		return true;
	}

	protected function publishSourceFiles($sourcePathMasks, $packageName, $packageRelativeDirName)
	{
		foreach ((array)$sourcePathMasks as $sourcePathMask) {
			if (isset($sourcePathMask['external']) && $sourcePathMask['external']) {
				continue;
			}

			$mask = isset($sourcePathMask['src']) ? $sourcePathMask['src'] : $sourcePathMask;
			$mask = $packageRelativeDirName . '/' . $mask;
			$files = Manager::getFilesByRelativeMask($mask);
			foreach ($files as $file) {
				$this->publishFile($file, $packageName, $packageRelativeDirName);
			}
		}
	}

	protected function publishFile($srcPath, $packageName, $packageRelativeDirName, $scope = null) {
		# Получаем полный путь к исходному файлу
		$realSrcPath = Publisher::getRealFilePath($srcPath);

		# Получаем относительный URI опубликованного файла
		$srcPath = str_replace($packageRelativeDirName . '/', '', $srcPath);
		$relativeTargetPath = Publisher::getUri($srcPath, true, $packageName);
		//$relativeTargetPath = File::getUrl($srcPath, $packageName, $scope, true);

		# Получаем полный путь к опубликованному файлу
		$realTargetPath = Publisher::$basePath . DIRECTORY_SEPARATOR . $relativeTargetPath;

		# Получаем полный путь к каталогу, в который опубликуется файл
		$realTargetDir = dirname($realTargetPath);

		# Создаем каталог, в который публикуется файл
		if (!file_exists($realTargetDir) && !mkdir($realTargetDir, 0777, true)) {
			throw new \Exception('Can`t create directory: ' . $realTargetDir, 500);
		}

		# Публикуем файл
		if (!copy($realSrcPath, $realTargetPath)) {
			throw new \Exception('Can`t copy file.', 500);
		}

		return true;
	}
}